package com.cai310.lottery.service.lottery.keno.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.cai310.lottery.Constant;
import com.cai310.lottery.common.AgentAnalyseState;
import com.cai310.lottery.common.AgentDetailType;
import com.cai310.lottery.common.AgentLotteryType;
import com.cai310.lottery.common.ChaseState;
import com.cai310.lottery.common.EventLogType;
import com.cai310.lottery.common.FundDetailType;
import com.cai310.lottery.common.FundMode;
import com.cai310.lottery.common.Lottery;
import com.cai310.lottery.common.PlatformInfo;
import com.cai310.lottery.common.PrepaymentType;
import com.cai310.lottery.common.SchemePrintState;
import com.cai310.lottery.common.SchemeState;
import com.cai310.lottery.common.SecretType;
import com.cai310.lottery.common.ShareType;
import com.cai310.lottery.common.SubscriptionLicenseType;
import com.cai310.lottery.common.SubscriptionState;
import com.cai310.lottery.common.SubscriptionWay;
import com.cai310.lottery.common.TransactionType;
import com.cai310.lottery.common.WinningUpdateStatus;
import com.cai310.lottery.common.keno.IssueState;
import com.cai310.lottery.common.keno.WonType;
import com.cai310.lottery.common.keno.ZoomType;
import com.cai310.lottery.dao.lottery.PrintInterfaceDao;
import com.cai310.lottery.dao.lottery.SubscriptionDao;
import com.cai310.lottery.dao.lottery.keno.IssueDataDao;
import com.cai310.lottery.dao.lottery.keno.KenoSysConfigDao;
import com.cai310.lottery.dao.lottery.keno.SchemeDao;
import com.cai310.lottery.entity.lottery.ChasePlan;
import com.cai310.lottery.entity.lottery.SaleAnalyse;
import com.cai310.lottery.entity.lottery.Subscription;
import com.cai310.lottery.entity.lottery.keno.KenoPeriod;
import com.cai310.lottery.entity.lottery.keno.KenoScheme;
import com.cai310.lottery.entity.lottery.keno.KenoSysConfig;
import com.cai310.lottery.entity.lottery.keno.sdel11to5.SdEl11to5Scheme;
import com.cai310.lottery.entity.lottery.keno.ssc.SscScheme;
import com.cai310.lottery.entity.lottery.pl.PlScheme;
import com.cai310.lottery.entity.lottery.zc.SfzcScheme;
import com.cai310.lottery.entity.security.AdminUser;
import com.cai310.lottery.entity.user.Prepayment;
import com.cai310.lottery.entity.user.Transaction;
import com.cai310.lottery.entity.user.User;
import com.cai310.lottery.entity.user.UserRebate;
import com.cai310.lottery.exception.DataException;
import com.cai310.lottery.service.QueryService;
import com.cai310.lottery.service.ServiceException;
import com.cai310.lottery.service.lottery.ChasePlanEntityManager;
import com.cai310.lottery.service.lottery.SaleAnalyseEntityManager;
import com.cai310.lottery.service.lottery.keno.KenoPlayer;
import com.cai310.lottery.service.lottery.keno.KenoService;
import com.cai310.lottery.service.user.UserEntityManager;
import com.cai310.lottery.utils.BigDecimalUtil;
import com.cai310.lottery.utils.DozerMapperSingleton;
import com.cai310.lottery.web.controller.lottery.keno.KenoSchemeDTO;
import com.cai310.orm.Pagination;
import com.cai310.orm.XDetachedCriteria;
import com.cai310.spring.SpringContextHolder;
import com.cai310.utils.DateUtil;
import com.cai310.utils.ReflectionUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

@Transactional
public abstract class KenoServiceImpl<I extends KenoPeriod, S extends KenoScheme> implements KenoService<I, S> {
	@Resource
	private QueryService queryService;
	long time = System.currentTimeMillis();
	public void xiaolu(String word){
		System.out.println("----------"+word+"------------用时："+(System.currentTimeMillis()-time));
		time=System.currentTimeMillis();
	}
	public I endKenoPeriod(I issueData){
		
		issueData = this.findIssueDataById(issueData.getId());
		if (issueData.getPrevPreriodId() != null) {
			logger.info("==========：处理过期未追的追号==========");
			this.doChasePlan(issueData.getPrevPreriodId(), null);// 对上期未追号的追号进行处理.
		}
		logger.info("==========：处理非中后停追的追号==========");
		// cyy-c 2011-04-14增加未出票撤单功能
		// 读取本期中的投注方案
		List<S> schemeList = null;
		schemeList = this.getSchemeByIssueNumber(issueData.getPeriodNumber().trim());
		xiaolu("完成交易-读取方案");
		if (schemeList != null && !schemeList.isEmpty()) {
			for (S scheme : schemeList) {
				xiaolu("完成交易-处理方案"+scheme.getId());
			    if(scheme.getSchemePrintState().equals(SchemePrintState.SUCCESS)){
			    	this.commitTransactionScheme(scheme.getId(), null);
			    }else{
			    	if(scheme.getState().equals(com.cai310.lottery.common.SchemeState.CANCEL)||scheme.getState().equals(com.cai310.lottery.common.SchemeState.REFUNDMENT)){
			    		
			    	}else{
				    	this.cancelScheme(scheme.getId(), null);
			    	}
			    }
			    xiaolu("完成交易-处理完方案"+scheme.getId());
			}
		}
		this.doChasePlan(issueData.getId(), false);
		issueData.setState(IssueState.ISSUE_SATE_END);
		logger.info("==========：[" + issueData.getPeriodNumber() + "]期上升为截止期==========");
		return this.saveIssueData(issueData);
	}
	public void updatePeriodPrize(I issueData,KenoPlayer kenoPlayer) throws DataException{
		issueData = this.findIssueDataById(issueData.getId());
		String results = issueData.getResults();
		// 读取本期中的投注方案
		List<S> schemeList = null;
		schemeList = this.getSchemeByIssueNumber(issueData.getPeriodNumber().trim());
		if (schemeList != null && !schemeList.isEmpty()) {
			for (S scheme : schemeList) {
				 xiaolu("更新中奖-处理完方案"+scheme.getId());
				if (!scheme.isWon()) {
					kenoPlayer.calculatePrice(scheme, results);
					ZoomType zoomType = ((KenoScheme)scheme).getZoomType();
					WonType wonType = ((KenoScheme)scheme).getWonType();
					if (scheme.isWon()) {
						BigDecimal prizeAfterTax= scheme.getPrizeAfterTax();
						scheme.setPrize(wonType.getZoomMoney(zoomType.getZoomMoney(scheme.getPrize())));
						scheme.setPrizeAfterTax(wonType.getZoomMoney(zoomType.getZoomMoney(scheme.getPrizeAfterTax())));
						scheme.setPrizeDetail(scheme.getPrizeDetail()+"税后总奖金：原始税后奖金("+prizeAfterTax+")*"+zoomType.getTypeName()+"*"+wonType.getTypeName()+"="+scheme.getPrizeAfterTax());
						scheme.setWinningUpdateStatus(WinningUpdateStatus.WINNING_UPDATED);
					}else{
						scheme.setWinningUpdateStatus(WinningUpdateStatus.WINNING_UPDATED);
					}
					this.saveScheme(scheme);
				}
				 xiaolu("更新中奖-处理完方案"+scheme.getId());
			}
		}

		logger.info("==========：处理开号后停追的追号==========");
		// 找出期间所有设置了 开号后停追的追号
		// 判断投注是否中奖
		// 停追
		List<ChasePlan> chasePlanList = getOutNumStopChasePlan(true, issueData);
		if (chasePlanList != null && !chasePlanList.isEmpty()) {
			for (ChasePlan chasePlan : chasePlanList) {
				// 开号后停止 且 未进行追号记录
				if (chasePlan.isOutNumStop() && chasePlan.getChasedSize() < 0) {
					boolean isWon = kenoPlayer.calculatePrice(chasePlan.getMode(), chasePlan.getContent(),
							chasePlan.getNextMultiple(), chasePlan.getPlayType(), results);
					if (isWon) {
						logger.info("==========开号后停追的追号ID======已停====" + chasePlan.getId());
						synchronized (Constant.STOPCHASE) {
							this.stopChasePlan(chasePlan.getId(), "开号停追");
						}
					}
				}
			}
		}

		logger.info("==========：处理中后停追的追号==========");
		this.doChasePlan(issueData.getId(), true);

		// 更新为已更新中奖方案
		this.updateIssueState(issueData.getId(), IssueState.ISSUE_SATE_ACCOUNT_PRIZE);
		logger.info("==========：[" + issueData.getPeriodNumber() + "]期上升为更新中奖状态==========");
	}
	public void sendPrize(I issueData){
		// 读取本期中的投注方案
		issueData = this.findIssueDataById(issueData.getId());
		List<S>  schemeList = this.getSchemeByIssueNumber(issueData.getPeriodNumber().trim());
		if (schemeList != null && !schemeList.isEmpty()) {
			for (S scheme : schemeList) {
				if (scheme.isWon() && !scheme.isPrizeSended()) {
					// 对已经计算方案但尚未派奖的方案进行派奖
					this.sendUserPrice(scheme);
				}
			}
		}
		// 更新为已派奖方案
		this.updateIssueState(issueData.getId(), IssueState.ISSUE_SATE_SEND_PRIZE);
		logger.info("==========：[" + issueData.getPeriodNumber() + "]期上升为派奖状态==========");

	}
	protected List<ChasePlan> getOutNumStopChasePlan(boolean outNumStop, I issueData) {
		DetachedCriteria criteria = DetachedCriteria.forClass(ChasePlan.class);
		if (outNumStop) {
			criteria.add(Restrictions.eq("outNumStop", outNumStop));
		}
		criteria.add(Restrictions.eq("lotteryType", this.getLottery()));
		// criteria.add(Restrictions.ge("curPeriodId",
		// Long.valueOf(issueData.getPeriodNumber())));
		criteria.add(Restrictions.eq("state", ChaseState.RUNNING));
		List<ChasePlan> list = queryService.findByDetachedCriteria(criteria);
		return list;
	}
	/**
	 * 自动追号
	 */
	protected void doChasePlan(long curPeriodId, Boolean iswonStop) {
		DetachedCriteria criteria = DetachedCriteria.forClass(ChasePlan.class);
		criteria.setProjection(Projections.property("id"));
		if (iswonStop != null)
			criteria.add(Restrictions.eq("wonStop", iswonStop));
		criteria.add(Restrictions.eq("lotteryType", getLottery()));
		criteria.add(Restrictions.eq("curPeriodId", curPeriodId));
		criteria.add(Restrictions.eq("state", ChaseState.RUNNING));
		List<Long> list = queryService.findByDetachedCriteria(criteria);
		if (list != null && !list.isEmpty()) {
			for (Long chasePlanId : list) {
				this.handChasePlan(chasePlanId);
			}
		}
	}
	// protected ChasePlanDao<C> chasePlanDao;
	protected IssueDataDao<I> issueDataDao;
	protected SchemeDao<S> schemeDao;
	protected KenoSysConfigDao kenoSysConfigDao;
	@Autowired
	protected ChasePlanEntityManager chasePlanEntityManager;

	@Autowired
	protected UserEntityManager userManager;
	@Autowired
	protected SaleAnalyseEntityManager saleAnalyseEntityManager;
	@Autowired
	protected PrintInterfaceDao printInterfaceDao;

	@Autowired
	protected SubscriptionDao subscriptionDao;

	protected Logger logger = LoggerFactory.getLogger(getClass());

	public abstract Lottery getLottery();

	/**
	 * 把方案写到打印接口
	 * 
	 * @param scheme
	 */
	protected abstract void pushToPrintInterface(S scheme, I issueData);

	protected abstract ChasePlan setChasePlanPlayType(ChasePlan chasePlan, KenoSchemeDTO schemeDTO);

	/**
	 * 据键值读取配置信息项
	 * 
	 * @param id
	 *            键值
	 * @return 一条配置信息
	 */
	@Transactional(readOnly = true)
	public KenoSysConfig getSysConfigByKey(String id) {
		return kenoSysConfigDao.get(id);
	}

	/**
	 * 保存或更新配置信息
	 * 
	 * @param entity
	 *            一条配置信息
	 */
	@Transactional(readOnly = true)
	public void getSysConfigByKey(KenoSysConfig entity) {
		kenoSysConfigDao.save(entity);
	}

	/**
	 * 据期号读取期号数据
	 * 
	 * @param issueNumber
	 *            期号数据
	 */
	@Transactional(readOnly = true)
	public I findByIssueNumber(String issueNumber) {
		return issueDataDao.findDataByNumber(issueNumber);
	}

	/**
	 * 读取当前期数据
	 * 
	 * @param dateNow
	 *            当前系统时间
	 * @param beforeTime
	 *            提前开售和截止时间（单位：分钟）
	 * @return 前期数据
	 */
	@Transactional(readOnly = true)
	public I getCurrIssueData(Date dateNow, int beforeTime) {
		return issueDataDao.findCurrentData(dateNow, beforeTime);
	}

	/**
	 * 读取最后一期的期号数据
	 * 
	 * @return 最后一期的期号数据
	 */
	@Transactional(readOnly = true)
	public I getLastIssue() {
		return issueDataDao.findLastIssue();
	}

	/**
	 * 读取最后一期开奖期号数据
	 * 
	 * @return 最后一期开奖期号数据
	 */
	@Transactional(readOnly = true)
	public I getLastResultIssueData() {
		return issueDataDao.findLastResultIssueData();
	}

	/**
	 * 读取需要派奖的期号列表
	 * 
	 * @param dateNow
	 *            当前系统时间
	 * @return 需要派奖的期号列表
	 */
	@Transactional(readOnly = true)
	public List<I> getCanSendPrize(Date dateNow) {
		return issueDataDao.findIssueDataList(dateNow, IssueState.ISSUE_SATE_ACCOUNT_PRIZE);
	}

	/**
	 * 开售当前期
	 * 
	 * @param dateNow
	 *            当前系统时间
	 * @param beforeTime
	 *            提前开售和截止时间（单位：分钟）
	 */
	public void openCurrentIssue(Date dateNow, int beforeTime) {
		I issueData = issueDataDao.findCurrentData(dateNow, beforeTime);
		if (issueData != null) {
			if (issueData.getState().getStateValue() < IssueState.ISSUE_SATE_CURRENT.getStateValue()) {
				issueData.setState(IssueState.ISSUE_SATE_CURRENT);
				issueDataDao.save(issueData);
			}
		}
	}

	@Transactional(readOnly = true)
	public I findCurrentData(Date dateNow, int beforeTime) {
		return issueDataDao.findCurrentData(dateNow, beforeTime);
	}

	/**
	 * 保存期号数据
	 * 
	 * @param issueData
	 *            要保存的期号数据
	 */
	@Transactional
	public I saveIssueData(I issueData) {
		I newIssueData = issueDataDao.save(issueData);
		return newIssueData;
	}

	/**
	 * 读取可以更新中奖的期号列表
	 * 
	 * @param dateNow
	 *            当前系统时间
	 * @return 可以更新中奖的期号列表
	 */
	@Transactional(readOnly = true)
	public List<I> getCanOpenPrize(Date dateNow) {
		return issueDataDao.findIssueDataList(dateNow, IssueState.ISSUE_SATE_RESULT);
	}

	@Transactional(readOnly = true)
	public List<I> getCanOpenResults() {
		DetachedCriteria criteria = DetachedCriteria.forClass(issueDataDao.getEntityClass());
		criteria.add(Restrictions.lt("endedTime", new Date()));
		criteria.add(Restrictions.gt("endedTime", DateUtils.addDays(new Date(), -1)));
		criteria.add(Restrictions.eq("state", IssueState.ISSUE_SATE_END));
		criteria.addOrder(Order.desc("id"));
		return issueDataDao.findByDetachedCriteria(criteria, true);
	}

	/**
	 * 更新期号状态
	 * 
	 * @param issueId
	 *            期号ID
	 * @param state
	 *            期号状态
	 */
	public void updateIssueState(Long issueId, IssueState state) {
		issueDataDao.updateIssueState(issueId, state);
	}

	public List<I> findKenoPeriodByCriteria(DetachedCriteria criteria, Integer num) {
		if (null == num) {
			return issueDataDao.findByDetachedCriteria(criteria);
		}
		return issueDataDao.findByDetachedCriteria(criteria, 0, num);
	}

	/**
	 * 据期号读取方案列表
	 * 
	 * @param issueNumber
	 *            期号
	 * @return 方案列表
	 */
	@Transactional(readOnly = true)
	public List<S> getSchemeByIssueNumber(String issueNumber) {
		return schemeDao.findByIssueNumber(issueNumber);
	}
	/**
	 * 据读未统计方案列表
	 * 
	 * @param periodId
	 *            期号
	 * @return 方案列表
	 */
	@Transactional(readOnly = true)
	public List<S> getUnAgentAnalyseScheme(Long periodId) {
		return schemeDao.findUnAnalyes(periodId);
	}
	/**
	 * 据读未派奖方案列表
	 * 
	 * @param periodId
	 *            期号
	 * @return 方案列表
	 */
	@Transactional(readOnly = true)
	public List<S> findUnSendPrize(Long periodId) {
		return schemeDao.findUnSendPrize(periodId);
	}
	/**
	 * 据期号读取出票失败方案列表
	 * 
	 * @param issueNumber
	 *            期号
	 * @return 出票失败方案列表
	 */
	@Transactional(readOnly = true)
	public List<S> getPrintFailSchemeByIssueNumber(String issueNumber) {
		return schemeDao.findPrintFailByIssueNumber(issueNumber);
	}

	/**
	 * 保存方案
	 * 
	 * @param scheme
	 *            需保存的方案
	 */
	public void saveScheme(S scheme) {
		schemeDao.save(scheme);
	}

	/**
	 * 据方案号向用户派奖
	 * 
	 * @param scheme
	 *            需派奖的方案
	 */
	public void sendUserPrice(S scheme) {
		scheme.setPrizeSended(true);
		// zhuhui motify by 2011-05-03 增加 派送奖金时间
		scheme.setPrizeSendTime(new Date());
		// xxxc motify by 2012-09-03 增加 派送奖金状态
		scheme.setWinningUpdateStatus(WinningUpdateStatus.PRICE_UPDATED);
		this.schemeDao.save(scheme);
		User user = userManager.getUser(scheme.getSponsorId());
		// cyy-c 2011-04-13 加入高频彩派奖写入加入表
		// 加载正常的加入对象列表
		List<Subscription> subscriptionList = findSubscriptionsOfScheme(scheme.getId(), SubscriptionState.NORMAL);// /读出跟单列表
		if (null != subscriptionList && !subscriptionList.isEmpty()) {// 正常情况高频彩只有一个加入
			Subscription subscription = subscriptionList.get(0);// 读取第一个加入
			subscription.setBonus(scheme.getPrizeAfterTax());// 把方案税后奖金设上
			subscriptionDao.save(subscription);// 保存
			userManager.oprUserMoney(user, scheme.getPrizeAfterTax(), this.getLottery()
					.getLotteryName() + "中奖:方案号:[" + scheme.getSchemeNumber() + "]", FundMode.IN,
					FundDetailType.SCHEME_BONUS, null);// 派发奖金
		}
	}

	/**
	 * 读取需要截止的期号列表
	 * 
	 * @param dateNow
	 *            当前系统时间
	 * @return 需要截止的期号列表
	 */
	@Transactional(readOnly = true)
	public List<I> getCanCloseIssue(Date dateNow) {
		return issueDataDao.findIssueDataList(dateNow, IssueState.ISSUE_SATE_SEND_PRIZE);
	}

	/**
	 * 用户投注
	 * 
	 * @param scheme
	 *            投注的方案号
	 * @param user
	 *            投注的用户
	 * @throws DataException
	 *             投注异常信息
	 */
	public void userBet(S scheme, User user) throws DataException {
		BigDecimal cost = new BigDecimal(scheme.getSchemeCost());
		userManager.oprUserMoney(user, cost, scheme.getLotteryType().getLotteryName() + "投注",
				FundMode.OUT, FundDetailType.SUBSCRIPTION, null);
		// 增加用户消费金额
		schemeDao.save(scheme);
	}

	/**
	 * 保存追号内容
	 * 
	 * @param chasePlan
	 *            追号内容
	 */
	public void userChasePlan(ChasePlan chasePlan) {
		chasePlanEntityManager.saveChasePlan(chasePlan);
	}

	/**
	 * 分页读取期数
	 * 
	 * @param criteria
	 * @param pagination
	 * @return
	 */
	@Transactional(readOnly = true)
	public Pagination findByCriteriaAndPagination(XDetachedCriteria criteria, Pagination pagination) {
		return issueDataDao.findByCriteriaAndPagination(criteria, pagination);
	}

	@Transactional(readOnly = true)
	public I findIssueDataById(Long id) {
		return issueDataDao.get(id);
	}

	@Transactional(readOnly = true)
	public Class<I> getIssueDataClass() {
		return issueDataDao.getEntityClass();
	}

	@Transactional(readOnly = true)
	public Class<S> getSchemeClass() {
		return schemeDao.getEntityClass();
	}

	/**
	 * 据ID读取追号内容
	 */
	@Transactional(readOnly = true)
	public ChasePlan findChasePlanById(Long id) {
		return chasePlanEntityManager.getChasePlan(id);
	}

	/**
	 * new一个新方案对象
	 * 
	 * @return 方案对象
	 */
	@Transactional(readOnly = true)
	public S newInstance(KenoSchemeDTO schemeDTO) {
		S scheme;
		try {
			scheme = schemeDao.getEntityClass().newInstance();
		} catch (Exception e) {
			throw new ServiceException(e.getMessage());
		}
		scheme.setContent(schemeDTO.getContent());
		scheme.setSchemeCost(schemeDTO.getSchemeCost());
		scheme.setUnits(schemeDTO.getUnits());
		scheme.setMultiple(schemeDTO.getMultiple());
		scheme.setPeriodId(schemeDTO.getPeriodId());
		scheme.setCreateTime(new Date());
		scheme.setPrizeSended(false);
		scheme.setWon(false);
		scheme.setSponsorId(schemeDTO.getSponsorId());
		scheme.setMode(schemeDTO.getMode());
		scheme.setSchemePrintState(SchemePrintState.UNPRINT);
		scheme.setShareType(ShareType.SELF);
		scheme.setSecretType(schemeDTO.getSecretType());
		scheme.setSubscriptionLicenseType(schemeDTO.getSubscriptionLicenseType());
		scheme.setSubscriptionPassword(schemeDTO.getSubscriptionPassword());
		if (StringUtils.isNotBlank(schemeDTO.getContent())) {
			try {
				scheme.uploadContent(schemeDTO.getContent());
			} catch (DataException e) {
				throw new ServiceException(e.getMessage());
			}
		}

		scheme.setState(SchemeState.UNFULL);
		scheme.setWinningUpdateStatus(WinningUpdateStatus.NONE);

		return scheme;
	}

	@Transactional(readOnly = true)
	public S newInstance(ChasePlan chasePlan) {
		S scheme;
		try {
			scheme = schemeDao.getEntityClass().newInstance();
		} catch (Exception e) {
			throw new ServiceException(e.getMessage());
		}
		scheme.setContent(chasePlan.getContent());
		scheme.setUnits(chasePlan.getUnits());

		scheme.setSponsorId(chasePlan.getUserId());
		scheme.setSponsorName(chasePlan.getUserName());

		scheme.setChaseId(chasePlan.getId());

		scheme.setMode(chasePlan.getMode());
		scheme.setSchemePrintState(SchemePrintState.UNPRINT);
		scheme.setShareType(ShareType.SELF);
		scheme.setSecretType(SecretType.FULL_SECRET);
		scheme.setSubscriptionLicenseType(SubscriptionLicenseType.PUBLIC_LICENSE);
		if (StringUtils.isNotBlank(chasePlan.getContent())) {
			try {
				scheme.uploadContent(chasePlan.getContent());
			} catch (DataException e) {
				throw new ServiceException(e.getMessage());
			}
		}
		scheme.setId(null);
		return scheme;
	}

	/**
	 * 发起方案
	 * 
	 * @param dto
	 * @return
	 */
	public S createScheme(KenoSchemeDTO schemeDTO) {
		I issueData = issueDataDao.get(schemeDTO.getPeriodId());
		checkConformPeriodInitConfig(issueData, schemeDTO);

		User user = userManager.getUser(schemeDTO.getSponsorId());
		checkUser(user, schemeDTO);

		S scheme = null;
		try {
			scheme = schemeDao.getEntityClass().newInstance();
		} catch (InstantiationException e) {
			throw new ServiceException(e.getMessage());
		} catch (IllegalAccessException e) {
			throw new ServiceException(e.getMessage());
		}
		scheme = newInstance(schemeDTO);
		 //用户来源
        if(null!=schemeDTO.getPlatform()){
        	scheme.setPlatform(schemeDTO.getPlatform());
        }
		scheme.setPeriodNumber(issueData.getPeriodNumber());
		scheme.setSponsorName(user.getUserName());
		StringBuilder sb = new StringBuilder(50);
		sb.append("【").append(scheme.getLotteryType().getLotteryName()).append("】").append(scheme.getPeriodNumber())
				.append("期").append(scheme.getMode().getModeName()).append(scheme.getShareType().getShareName())
				.append("方案.");
		Transaction tran = userManager.createTransaction(TransactionType.SCHEME, sb.toString());
		scheme.setTransactionId(tran.getId());
		scheme.setZoomType(schemeDTO.getZoomType());
		scheme.setWonType(schemeDTO.getWonType());
		//自动出票
		scheme.setSchemePrintState(SchemePrintState.SUCCESS);
		// 追号处理
		if (schemeDTO.isChase()) {
			Lottery lotteryType = scheme.getLotteryType();
			ChasePlan chasePlan = new ChasePlan();
			chasePlan.setPlatform(schemeDTO.getPlatform());
			chasePlan.setZoomType(schemeDTO.getZoomType());
			chasePlan.setLotteryType(lotteryType);
			chasePlan.setState(ChaseState.RUNNING);
			chasePlan.setUserId(user.getId());
			chasePlan.setUserName(user.getUserName());
			chasePlan.setTotalCost(schemeDTO.getTotalCostOfChase());
			chasePlan.setRandom(schemeDTO.isRandomOfChase());
			chasePlan.setRandomUnits(schemeDTO.getRandomUnitsOfChase());
			chasePlan.setHasDan(false);
			chasePlan.setWonStop(schemeDTO.isWonStopOfChase());
			chasePlan.setPrizeForWonStop(schemeDTO.getPrizeForWonStopOfChase());
			chasePlan.setOutNumStop(schemeDTO.isOutNumStop());
			chasePlan = this.setChasePlanPlayType(chasePlan, schemeDTO);
			chasePlan.setUnits(schemeDTO.getUnits());
			chasePlan.setMode(schemeDTO.getMode());
			chasePlan.setContent(schemeDTO.getContent());
			chasePlan.setSchemeCost(schemeDTO.getSchemeCost());
			chasePlan.setCapacityProfit(schemeDTO.getCapacityProfit());

			try {
				chasePlan.setMultiples(schemeDTO.getMultiplesOfChase());
			} catch (DataException e) {
				throw new ServiceException(e.getMessage());
			}
			StringBuilder sb1 = new StringBuilder(20);
			sb1.append("发起【").append(lotteryType.getLotteryName()).append("】追号.").append("已冻结金额：")
					.append(schemeDTO.getZoomType().getZoomMoney(BigDecimalUtil.valueOf(chasePlan.getTotalCost())) + " 元  ").append(" 其中：");

			tran = userManager.createTransaction(TransactionType.CHASE, sb1.toString());
			chasePlan.setTransactionId(tran.getId());
			Prepayment chasePrepayment = userManager.createKenoPrepayment(chasePlan.getTransactionId(),
					user.getId(), BigDecimal.valueOf(chasePlan.getTotalCost()), PrepaymentType.CHASE,
					FundDetailType.CHASE, sb1.toString(),schemeDTO.getPlatform(),this.getLottery(),schemeDTO.getZoomType());

			// 关联 chasePrepayment.getId()
			chasePlan.setPrepaymentId(chasePrepayment.getId());

			if (schemeDTO.getStartChasePeriodId() != null) {
				if (scheme.getPeriodId().longValue() != schemeDTO.getStartChasePeriodId().longValue()) {
					I period = this.findIssueDataById(schemeDTO.getStartChasePeriodId());
					chasePlan.setCurPeriodId(period.getPrevPreriodId());
					chasePlan.setChasedCost(0);
					chasePlan = chasePlanEntityManager.saveChasePlan(chasePlan);
					scheme = null;
					return null;
				}
			}

			try {
				Integer nextMultiple = chasePlan.getNextMultiple();// 新的方案倍数
				if (nextMultiple == null || nextMultiple.intValue() <= 0) {
					throw new DataException("追号方案倍数异常！");
				}
				scheme.setMultiple(nextMultiple);
				scheme.setSchemeCost(scheme.getUnits() * nextMultiple * 2);

			} catch (DataException e) {
				throw new ServiceException(e.getMessage(), e);
			}

			try {
				chasePlan.chase(scheme.getPeriodId(), scheme.getSchemeCost());
			} catch (DataException e) {
				throw new ServiceException(e.getMessage());
			}

			chasePlan = chasePlanEntityManager.saveChasePlan(chasePlan);

			// 认购第一期方案

			scheme.setChaseId(chasePlan.getId());
			BigDecimal subscriptionCost = BigDecimal.valueOf(scheme.getSchemeCost());
			try {
				scheme.subscription(subscriptionCost);
			} catch (DataException e) {
				throw new ServiceException(e.getMessage(), e);
			}
			scheme = schemeDao.save(scheme);

			sb.setLength(0);
			sb.append("认购【").append(scheme.getLotteryType().getLotteryName()).append("】")
					.append(scheme.getPeriodNumber()).append("期").append("方案[").append(scheme.getSchemeNumber())
					.append("],认购金额从追号预付款(追号冻结资金)中扣除. 其中：");
			Prepayment subscriptionPrepayment = userManager.transferKenoPrepayment(scheme.getTransactionId(),
					chasePlan.getPrepaymentId(), subscriptionCost, PrepaymentType.SUBSCRIPTION,
					FundDetailType.SUBSCRIPTION, sb.toString(),schemeDTO.getPlatform(),this.getLottery(),schemeDTO.getZoomType());

			saveSubscription(scheme, subscriptionPrepayment.getId(), user, subscriptionCost, SubscriptionWay.INITIATE,schemeDTO.getPlatform());

			return scheme;
		} else {
			BigDecimal subscriptionCost = BigDecimal.valueOf(scheme.getSchemeCost());
			try {
				scheme.subscription(subscriptionCost);
			} catch (DataException e) {
				throw new ServiceException(e.getMessage(), e);
			}
			scheme = schemeDao.save(scheme);
            
			createSubscription(scheme, user, subscriptionCost, SubscriptionWay.INITIATE,schemeDTO.getPlatform(),schemeDTO.getZoomType());

			return scheme;
		}
	}

	/**
	 * 创建认购记录
	 * 
	 * @param scheme
	 *            认购 的方案
	 * @param user
	 *            认购的用户
	 * @param cost
	 *            认购的金额
	 * @param way
	 *            认购的方式
	 * @return 认购记录
	 */
	protected Subscription createSubscription(S scheme, User user, BigDecimal cost, SubscriptionWay way,PlatformInfo platform,ZoomType zoomType) {
		StringBuilder sb = new StringBuilder(50);
		sb.append("认购方案[").append(scheme.getSchemeNumber()).append("].");
		Prepayment prepayment = userManager.createKenoPrepayment(scheme.getTransactionId(), user.getId(), cost,
				PrepaymentType.SUBSCRIPTION, FundDetailType.SUBSCRIPTION, sb.toString(),platform,this.getLottery(),zoomType);

		return saveSubscription(scheme, prepayment.getId(), user, cost, way,platform);
	}

	/**
	 * 创建认购记录
	 * 
	 * @param scheme
	 *            认购的方案
	 * @param prepaymentId
	 *            预付款ID
	 * @param user
	 *            认购的用户
	 * @param cost
	 *            认购 的金额
	 * @param way
	 *            认购的方式
	 * @return 认购记录
	 */
	protected Subscription saveSubscription(S scheme, Long prepaymentId, User user, BigDecimal cost, SubscriptionWay way,PlatformInfo platform) {
		Subscription subscription = new Subscription();
		subscription.setUserId(user.getId());
		subscription.setUserName(user.getUserName());
		subscription.setCost(cost);
		subscription.setState(SubscriptionState.NORMAL);
		subscription.setWay(way);
		subscription.setLotteryType(scheme.getLotteryType());
		subscription.setSchemeId(scheme.getId());
		subscription.setPrepaymentId(prepaymentId);
		subscription.setPlatform(platform);
		return subscriptionDao.save(subscription);
	}

	/**
	 * 验证用户是否符合发起方案的相关条件
	 * 
	 * @param user
	 *            用户对象
	 * @param schemeDTO
	 *            发起方案的数据传输对象
	 */
	@Transactional(readOnly = true)
	protected void checkUser(User user, KenoSchemeDTO schemeDTO) {
		if (user.isLocked())
			throw new ServiceException("您的帐户已经被锁定,不能发起方案.如有什么疑问,请联系我们的客服人员.");
		BigDecimal totalCost = null;
		if (schemeDTO.isChase()) {
			totalCost = schemeDTO.getZoomType().getZoomMoney(BigDecimal.valueOf(schemeDTO.getTotalCostOfChase()));
		} else {
			totalCost = schemeDTO.getZoomType().getZoomMoney(BigDecimal.valueOf(schemeDTO.getSchemeCost()));
		}
		BigDecimal allAccountBalance = userManager.getUser(user.getId()).getRemainMoney();

		BigDecimal remainMoney = (allAccountBalance != null) ? allAccountBalance : BigDecimal.ZERO;
		if (remainMoney.doubleValue() < totalCost.doubleValue())
			throw new ServiceException("对不起,完成此次交易共需要[" + Constant.MONEY_FORMAT.format(totalCost) + "]元,您的账户余额只有["
					+ Constant.MONEY_FORMAT.format(remainMoney) + "]元,不够支付此次交易.");
	}

	@Transactional(readOnly = true)
	protected void checkConformPeriodInitConfig(I issueData, KenoSchemeDTO schemeDTO) {
		if (null == issueData) {
			throw new ServiceException("期号为空");
		}
		if (isPause()) {
			throw new ServiceException("对不起,系统暂停销售！");
		}
		if (issueData.isSaleEnded()) {
			throw new ServiceException("对不起," + issueData.getPeriodNumber() + "已经截止！");
		}
		Long i = (new Date()).getTime() - Long.valueOf(schemeDTO.getBeforeTime() * 1000 * 60);
		if (issueData.getEndedTime().getTime() < (new Date()).getTime()
				- Long.valueOf(schemeDTO.getBeforeTime() * 1000 * 60)) {
			throw new ServiceException("对不起," + issueData.getPeriodNumber() + "已经截止！");
		}
	}

	@Transactional(readOnly = true)
	public void setUserManager(UserEntityManager userManager) {
		this.userManager = userManager;
	}

	@Transactional(readOnly = true)
	public I getNextPeriod(long periodId) {
		return this.issueDataDao.findNextIssueData(periodId);
	}

	/**
	 * 查询列表
	 * 
	 * @param criteria
	 * @return
	 */
	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public List<S> findByCriteria(XDetachedCriteria criteria) {
		return schemeDao.findByDetachedCriteria(criteria);
	}

	/**
	 * 更新开奖号码
	 * 
	 * @param issueData
	 */
	public void updateResults(I issueData) {
		I oldIssueData = issueDataDao.get(issueData.getId());
		if (oldIssueData != null) {
			if (!StringUtils.isBlank(issueData.getResults())) {
				// if(!oldIssueData.isDrawed()){
				// oldIssueData.setDrawed(true);
				// }
				if (oldIssueData.getState().getStateValue() < IssueState.ISSUE_SATE_RESULT.getStateValue()) {
					oldIssueData.setState(IssueState.ISSUE_SATE_RESULT);
				}
				// oldIssueData.addAfterSaleFlags(AfterSaleFlag.RESULT_UPDATED.getFlagValue());
				oldIssueData.setResults(issueData.getResults());
			}
			issueDataDao.save(oldIssueData);
		}
	}

	/**
	 * 暂停/启动销售
	 */
	public void pauseOrStartLottery() {
		KenoSysConfig config = kenoSysConfigDao.get(this.getLottery().getKey() + KenoSysConfig.PAUSE);
		if (config != null) {
			String value = config.getKeyValue();
			if (!StringUtils.isBlank(value) && "true".equals(value.trim())) {
				config.setKeyValue("false");
			} else {
				config.setKeyValue("true");
			}
			kenoSysConfigDao.save(config);
		}
	}

	/**
	 * 暂停/启动销售状态值
	 * 
	 * @return
	 */
	public String findPauseOrStart() {
		return isPause() ? "true" : "false";
	}

	@Transactional(readOnly = true)
	protected boolean isPause() {
		KenoSysConfig config = kenoSysConfigDao.get(this.getLottery().getKey() + KenoSysConfig.PAUSE);
		if (config != null) {
			String value = config.getKeyValue();
			if (!StringUtils.isBlank(value) && "true".equals(value.trim())) {
				return true;
			}
		}
		return false;
	}

	@Resource(name = "kenoSysConfigDao")
	public void setKenoSysConfigDao(KenoSysConfigDao kenoSysConfigDao) {
		this.kenoSysConfigDao = kenoSysConfigDao;
	}

	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public S cloneNewScheme(S oldScheme) {
		this.schemeDao.getSession().evict(oldScheme);
		oldScheme.setSubscriptions(null);
		S newScheme = DozerMapperSingleton.map(oldScheme, this.schemeDao.getEntityClass());
		newScheme.setId(null);
		oldScheme = schemeDao.get(oldScheme.getId());
		return newScheme;
	}

	@Transactional(readOnly = true)
	public S getLastChaseScheme(long chaseId) {
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(this.schemeDao.getEntityClass());
		detachedCriteria.add(Restrictions.eq("chaseId", chaseId));
		detachedCriteria.addOrder(Order.desc("id"));
		List<S> schemeList = this.schemeDao.findByDetachedCriteria(detachedCriteria, 0, 1, true);
		if (schemeList != null && schemeList.size() > 0) {
			return schemeList.get(0);
		}
		return null;
	}

	public void handChasePlan(long chasePlanId) {
		ChasePlan chasePlan = this.chasePlanEntityManager.getChasePlan(chasePlanId);
		S lastScheme = this.getLastChaseScheme(chasePlan.getId());
		if (null == lastScheme) {
			lastScheme = this.newInstance(chasePlan);
		}
		boolean canChase = true;
		if (chasePlan.isWonStop() && lastScheme.isWon() && lastScheme.getState() == SchemeState.SUCCESS) {
			if (chasePlan.getPrizeForWonStop() != null && chasePlan.getPrizeForWonStop() >= 0) {
				if (lastScheme.getPrize().intValue() >= chasePlan.getPrizeForWonStop()) {
					canChase = false;
				}
			}
		}
		if (!canChase) {
			this.stopChasePlan(chasePlan.getId(), "方案中奖");// 中奖后停止追号
		} else {
			// 获取将要追号的销售期，并判断是否允许发起追号方案
			I nexPeriod = this.getNextPeriod(chasePlan.getCurPeriodId());
			if (nexPeriod.isSaleEnded()) {
				this.stopChasePlan(chasePlan.getId(), "由于网络故障");
			} else {
				Integer multiple = chasePlan.getNextMultiple();
				if (multiple == null || multiple <= 0) {
					try {
						chasePlan.skip(nexPeriod.getId());
					} catch (DataException e) {
						throw new ServiceException(e.getMessage(), e);
					}
					this.userChasePlan(chasePlan);
					return;
				}
				// 创建新方案交易记录
				Transaction tran = userManager.createTransaction(TransactionType.SCHEME, "追号生成方案");
				// 从旧方案复制生成新方案
				S newScheme = null;
				if (null != lastScheme.getId()) {
					newScheme = this.cloneNewScheme(lastScheme);
				} else {
					newScheme = lastScheme;
				}
				try {
					newScheme.reset(nexPeriod, chasePlan, tran.getId());// 重置方案
				} catch (DataException e) {
					throw new ServiceException(e.getMessage(), e);
				}
				newScheme.setId(null);// 确认
				newScheme = schemeDao.save(newScheme);

				// 从追号预付款中扣除新追号方案的方案金额生成新的预付款
				StringBuffer memo = new StringBuffer(50);
				memo.append("自购追号，新追号方案[").append(newScheme.getSchemeNumber())
						.append("]，所需费用从追号预扣款(追号冻结资金)中转移。其中转移消费明细： ");
				Prepayment prepayment = userManager.transferKenoPrepayment(tran.getId(),
						chasePlan.getPrepaymentId(), BigDecimalUtil.valueOf(newScheme.getSchemeCost()),
						PrepaymentType.CHASE, FundDetailType.CHASE, memo.toString(),chasePlan.getPlatform(),this.getLottery(),chasePlan.getZoomType());

				// 修改重置认购对象，保存新的认购对象
				List<Subscription> joinList = findSubscriptionsOfScheme(lastScheme.getId(), null);

				if (null == joinList || joinList.isEmpty() || joinList.size() == 0) {
					Subscription subscription = new Subscription();
					subscription.setUserId(chasePlan.getUserId());
					subscription.setUserName(chasePlan.getUserName());
					subscription.setCost(BigDecimalUtil.valueOf(newScheme.getSchemeCost()));
					subscription.setState(SubscriptionState.NORMAL);
					subscription.setWay(SubscriptionWay.NORMAL);
					subscription.setLotteryType(newScheme.getLotteryType());
					subscription.setSchemeId(newScheme.getId());
					subscription.setPrepaymentId(prepayment.getId());
					subscriptionDao.save(subscription);
				} else {
					Subscription oldSubscrt = joinList.get(0);
					Subscription newSubscrt = cloneNewSubscription(oldSubscrt);
					newSubscrt.reset(newScheme, prepayment.getId());
					newSubscrt = subscriptionDao.save(newSubscrt);
				}

				// 更新追号数据
				try {
					chasePlan.chase(newScheme.getPeriodId(), newScheme.getSchemeCost().intValue());
				} catch (DataException e) {
					throw new ServiceException(e.getMessage(), e);
				}
				this.userChasePlan(chasePlan);
			}
		}
	}

	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public List<Subscription> findSubscriptionsOfScheme(final long schemeId, final SubscriptionState state) {
		final S scheme = getScheme(schemeId);
		DetachedCriteria criteria = DetachedCriteria.forClass(Subscription.class);
		criteria.add(Restrictions.eq("schemeId", schemeId));
		criteria.add(Restrictions.eq("lotteryType", scheme.getLotteryType()));
		if (state != null) {
			criteria.add(Restrictions.eq("state", state));
		}
		return subscriptionDao.findByDetachedCriteria(criteria);
	}

	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public Subscription cloneNewSubscription(Subscription oldSubscription) {
		subscriptionDao.getSession().evict(oldSubscription);
		Subscription newSubscrt = DozerMapperSingleton.map(oldSubscription, Subscription.class);
		newSubscrt.setId(null);
		return newSubscrt;
	}

	public void stopChasePlan(long chasePlanId, String memo) {
		ChasePlan chasePlan = this.chasePlanEntityManager.getChasePlan(chasePlanId);
		if (chasePlan == null)
			throw new ServiceException("追号记录不存在.");
		try {
			chasePlan.stop();
		} catch (DataException e) {
		}
		chasePlan = chasePlanEntityManager.saveChasePlan(chasePlan);

		int remainMoney = chasePlan.getTotalCost() - chasePlan.getChasedCost();

		StringBuilder sb = new StringBuilder(50);
		if (StringUtils.isNotBlank(memo)) {
			sb.append(memo + ",");
		}
		sb.append("停止编号为[").append(chasePlan.getId()).append("]的追号.");
		User user = userManager.getUser(chasePlan.getUserId());
		if (remainMoney > 0) {
			userManager.cancelPrepayment(chasePlan.getPrepaymentId(), FundDetailType.STOP_CHASE,
					sb.toString());
		}
	}

	@Transactional(readOnly = true)
	public List<I> findCanCloseIssue(Date time) {
		return this.issueDataDao.findCanCloseIssue(time);
	}

	/**
	 * 统计总方案数
	 * 
	 * @param periodId
	 * @return
	 */
	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public Integer calAllSchemeCount(Long periodId) {
		Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
		DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
		criteria.add(Restrictions.eq("periodId", periodId));
		criteria.setProjection(Projections.rowCount());
		List<Integer> resultList = schemeDao.findByDetachedCriteria(criteria);
		if (resultList != null && resultList.size() > 0) {
			return resultList.get(0);
		} else {
			return 0;
		}
	}

	/**
	 * 统计总销量
	 * 
	 * @param periodId
	 * @return
	 */
	@Transactional(readOnly = true)
	public Integer calAllSaleCount(Long periodId) {
		Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
		DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
		criteria.add(Restrictions.eq("periodId", periodId));
		criteria.add(Restrictions.eq("state", SchemeState.SUCCESS));
		criteria.setProjection(Projections.sum("schemeCost"));
		List<Integer> resultList = schemeDao.findByDetachedCriteria(criteria);
		if (null != resultList && !resultList.isEmpty()) {
			Integer count = resultList.get(0);
			if (null != count) {
				return count.intValue();
			} else {
				return 0;
			}
		} else {
			return 0;
		}
	}

	/**
	 * 总中奖数
	 * 
	 * @param periodId
	 * @return
	 */
	@Transactional(readOnly = true)
	public Integer calAllPrizeCount(Long periodId) {
		Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
		DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
		criteria.add(Restrictions.eq("periodId", periodId));
		criteria.add(Restrictions.eq("won", true));
		criteria.setProjection(Projections.rowCount());
		List<Integer> resultList = schemeDao.findByDetachedCriteria(criteria);
		if (resultList != null && resultList.size() > 0) {
			return resultList.get(0);
		} else {
			return 0;
		}
	}

	/**
	 * 统计成功案数
	 * 
	 * @param periodId
	 * @return
	 */
	@Transactional(readOnly = true)
	public Integer calSuccessSchemeCount(Long periodId) {
		Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
		DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
		criteria.add(Restrictions.eq("periodId", periodId));
		criteria.add(Restrictions.eq("state", SchemeState.SUCCESS));
		criteria.setProjection(Projections.rowCount());
		List<Integer> resultList = schemeDao.findByDetachedCriteria(criteria);
		if (resultList != null && resultList.size() > 0) {
			return resultList.get(0);
		} else {
			return 0;
		}
	}

	/**
	 * 得到可以追号的期数
	 * 
	 * @param dateNow
	 *            系统时间
	 * @param beforeTime
	 *            提前截至时间
	 * @param size
	 *            取的数
	 * @return 一条配置信息
	 */
	@Transactional(readOnly = true)
	public List<I> getCanChaseIssue(Date dateNow, Integer beforeTime, Integer size) {
		if (null == dateNow)
			return null;
		if (null == beforeTime)
			return null;
		if (null == size)
			return null;
		Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 0);
		DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
		criteria.add(Restrictions.not(Restrictions.eq("state", IssueState.ISSUE_SATE_FINISH)));
		criteria.add(Restrictions.gt("endedTime", DateUtils.addMinutes(dateNow, beforeTime)));
		criteria.addOrder(Order.asc("endedTime"));
		return issueDataDao.findByDetachedCriteria(criteria, 0, size, true);
	}

	/**
	 * 得到可以追号的期数
	 * 
	 * @param dateNow
	 *            系统时间
	 * @param beforeTime
	 *            提前截至时间
	 * @return 一条配置信息
	 */
	@Transactional(readOnly = true)
	public Integer getCanChaseIssueNum(Date dateNow, Integer beforeTime) {
		if (null == dateNow)
			return null;
		if (null == beforeTime)
			return null;
		Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 0);
		DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
		criteria.add(Restrictions.not(Restrictions.eq("state", IssueState.ISSUE_SATE_FINISH)));
		criteria.add(Restrictions.gt("endedTime", DateUtils.addMinutes(dateNow, beforeTime)));
		criteria.addOrder(Order.asc("endedTime"));
		criteria.setProjection(Projections.rowCount());
		List<Integer> resultList = schemeDao.findByDetachedCriteria(criteria, true);
		if (resultList != null && resultList.size() > 0) {
			return resultList.get(0);
		} else {
			return 0;
		}
	}

	/**
	 * 设置期号时间
	 * 
	 * @param dateStar
	 *            开始时间
	 * @param dateEnd
	 *            结束时间
	 * @param dateMin
	 *            操作时间（可以是负数，action判断）
	 * @return 一条配置信息
	 */
	public void oprIssueTime(Date dateStar, Date dateEnd, Integer dateMin) {
		if (null == dateStar)
			throw new ServiceException("起始日期为空");
		if (null == dateEnd)
			throw new ServiceException("结束日期为空");
		if (null == dateMin)
			throw new ServiceException("操作时间为空.");
		Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 0);
		DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
		criteria.add(Restrictions.ge("endedTime", dateStar));
		criteria.add(Restrictions.le("endedTime", dateEnd));
		criteria.addOrder(Order.asc("endedTime"));
		List<I> resultList = schemeDao.findByDetachedCriteria(criteria, true);
		if (resultList != null && resultList.size() > 0) {
			for (I issue : resultList) {
				issue.setStartTime(DateUtils.addMinutes(issue.getStartTime(), dateMin));
				issue.setEndedTime(DateUtils.addMinutes(issue.getEndedTime(), dateMin));
				issue.setPrizeTime(DateUtils.addMinutes(issue.getPrizeTime(), dateMin));
			}
		} else {
			if (null == dateMin)
				throw new ServiceException("找不到相应记录.");
		}
	}

	/**
	 * 方案撤单
	 */
	public S cancelScheme(Long id, AdminUser adminUser) {
		if (null == id)
			throw new ServiceException("方案ID为空.");
		S s = schemeDao.get(id);
		if (null == s)
			throw new ServiceException("找不到相应方案.");
		if (null == s.getSponsorId())
			throw new ServiceException("找不到相应方案的发起人.");
		if (null == s.getSchemeCost())
			throw new ServiceException("找不到相应方案的金额.");
		if (null == s.getPeriodId())
			throw new ServiceException("找不到相应方案的方案号.");
		Long issueId = s.getPeriodId();
		I issue = issueDataDao.get(issueId);
		if (null == issue)
			throw new ServiceException("找不到相应期号.");
		if (null == issue.getState())
			throw new ServiceException("相应期号没有销售状态.");
		if (SchemeState.CANCEL.equals(s.getState()))
			throw new ServiceException("不能重复撤单.");
		if (issue.isDrawed()) {
			// 期状态已经开奖不能撤单
			throw new ServiceException("期状态已经开奖不能撤单.");
		}
		s.setState(SchemeState.CANCEL);
		schemeDao.save(s);
		// cyy-c 2011-04-13 加入高频彩派奖写入加入表
		// 加载正常的加入对象列表
		List<Subscription> subscriptionList = findSubscriptionsOfScheme(s.getId(), SubscriptionState.NORMAL);// /读出跟单列表
		if (null != subscriptionList && !subscriptionList.isEmpty()) {// 正常情况高频彩只有一个加入
			Subscription subscription = subscriptionList.get(0);// 读取第一个加入
			subscription.setState(SubscriptionState.CANCEL);
			subscriptionDao.save(subscription);// 保存
		}
		StringBuilder cause = new StringBuilder(50);
		cause.append(adminUser == null ? "系统" : "管理员").append("撤销方案[").append(s.getSchemeNumber()).append("].");
		userManager.cancelTransaction(s.getTransactionId(), FundDetailType.CANCEL_SCHEME, cause.toString());


		return s;
	}
	/**
	 * 方案设为成功
	 */
	public S commitTransactionScheme(Long id, AdminUser adminUser) {
		if (null == id)
			throw new ServiceException("方案ID为空.");
		S s = schemeDao.get(id);
		userManager.commitTransaction(s.getTransactionId());
		return s;
	}

	
	public S forcePrint(Long schemeId) {
		S scheme= schemeDao.get(schemeId);
		if (scheme == null)
			throw new ServiceException("方案不存在.");
		scheme.setSendToPrint(true);// 送票
		scheme.setSchemePrintState(SchemePrintState.SUCCESS);// 出票
		scheme = schemeDao.save(scheme);
		return scheme;
	}
	public S agentAnalyse(Long schemeId) {
		S scheme= schemeDao.get(schemeId);
		if (scheme == null)
			throw new ServiceException("方案不存在.");
		scheme.setAgentAnalyseState(AgentAnalyseState.UPDATED);
		scheme = schemeDao.save(scheme);
		return scheme;
	}
	/**
	 * 取方案
	 * 
	 * @param scheme
	 */
	@Transactional(readOnly = true)
	public S getScheme(Long id) {
		return schemeDao.get(id);
	}

	/**
	 * 更新销售统计
	 * 
	 * @param issueData
	 * @throws DataException
	 */
	public void saleAnalye(I issueData) throws DataException {
		Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
		DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
		criteria.setProjection(Projections.property("id"));
		criteria.add(Restrictions.eq("periodId", issueData.getId()));
		List<Long> schemeIdList = schemeDao.findByDetachedCriteria(criteria);
		SaleAnalyse saleAnalyse = null;
		Integer playType = null;
		Map<String, SaleAnalyse> saleAnalyeMap = new HashMap<String, SaleAnalyse>();
		S s;
		SdEl11to5Scheme sdEl11to5Scheme =null;
		SscScheme sscScheme =null;
		Set<Long> updateSchemeSet = Sets.newHashSet();
		for (Long id : schemeIdList) {
			// ///key规则。如果没有playType key=userId_ 如果有key=userId_0或者userId_1或者。。。。
			s = getScheme(id);
			saleAnalyse = saleAnalyeMap.get(s.getSponsorId() + "_" + (null == playType ? "" : playType));
			if (null == saleAnalyse) {
				saleAnalyse = new SaleAnalyse();
			}
			updateSchemeSet.add(s.getId());
			if(Lottery.SDEL11TO5.equals(s.getLotteryType())){
				sdEl11to5Scheme=(SdEl11to5Scheme)s;
				playType=sdEl11to5Scheme.getBetType().ordinal();
			}else if(Lottery.SSC.equals(s.getLotteryType())){
				sscScheme=(SscScheme)s;
				playType=sscScheme.getBetType().ordinal();
			}
			// //公共属性
			saleAnalyse.setPeriodId(issueData.getId());
			saleAnalyse.setLottery(s.getLotteryType());
			saleAnalyse.setPeriodNumber(issueData.getPeriodNumber());
			saleAnalyse.setPlayType(playType);
			saleAnalyse.setUserId(s.getSponsorId());
			saleAnalyse.setUserName(s.getSponsorName());
			saleAnalyse.setEndedTime(issueData.getEndedTime());
			saleAnalyse.setYearNum(DateUtil.getYear(issueData.getEndedTime()));
			saleAnalyse.setQuarterNum(DateUtil.getQuarter(issueData.getEndedTime()));
			saleAnalyse.setMonthNum(DateUtil.getMonth(issueData.getEndedTime()));
			saleAnalyse.setWeekNum(DateUtil.getWeek(issueData.getEndedTime()));
			saleAnalyse.setSendCurrency(Boolean.FALSE);

			saleAnalyse.addSchemeCount(1);
			saleAnalyse.addSchemeCost(s.getSchemeCost().intValue());
			saleAnalyse.addSchemeWonPrize(s.getPrize());
			saleAnalyse.addSubscriptionCount(1);
			saleAnalyse.addSubscriptionCost(new BigDecimal(s.getSchemeCost()));
			saleAnalyse.addSubscriptionWonPrize(s.getPrize());
			if (SchemeState.SUCCESS.equals(s.getState())) {
				// 成功的方案
				saleAnalyse.addSchemeSuccessCount(1);
				saleAnalyse.addSchemeSuccessCost(s.getSchemeCost().intValue());
				saleAnalyse.addSchemeSuccessWonPrize(s.getPrize());

				saleAnalyse.addSubscriptionSuccessCount(1);
				saleAnalyse.addSubscriptionSuccessCost(new BigDecimal(s.getSchemeCost()));
				saleAnalyse.addSubscriptionSuccessWonPrize(s.getPrize());

			} else if (SchemeState.CANCEL.equals(s.getState())) {
				// 失败的方案
				saleAnalyse.addSchemeCancelCount(1);
				saleAnalyse.addSchemeCancelCost(s.getSchemeCost().intValue());
				saleAnalyse.addSchemeCancelWonPrize(s.getPrize());

				saleAnalyse.addSubscriptionCancelCount(1);
				saleAnalyse.addSubscriptionCancelCost(new BigDecimal(s.getSchemeCost()));
				saleAnalyse.addSubscriptionCancelWonPrize(s.getPrize());
			} else {
				// 数据错误
				throw new DataException("数据错误");
			}
			saleAnalyeMap.put(s.getSponsorId() + "_" + (null == playType ? "" : playType), saleAnalyse);
		}
		Set<String> keySet = saleAnalyeMap.keySet();
		List<SaleAnalyse> updateSaleAnalyse = Lists.newArrayList();
		for (String key : keySet) {
			updateSaleAnalyse.add(saleAnalyeMap.get(key));
		}
		saleAnalyseEntityManager.updateSaleAnalyse(updateSaleAnalyse, updateSchemeSet, issueData.getLotteryType());
	}

	// TODO
	@Override
	public List moneyAnalye(String startDate, String endDate) {
		String tableName = "Lotu_Sd_El11to5_Scheme";
		if (Lottery.EL11TO5.equals(this.getLottery())) {
			tableName = "lotu_el11to5_scheme";
		} else if (Lottery.QYH.equals(this.getLottery())) {
			tableName = "lotu_qyh_scheme";
		}
		StringBuffer sql = new StringBuffer();
		sql.append("select sum(t.prize) from " + tableName
				+ " t where 1=1 and t.won = 1 and t.state = 2 and t.sendtoprint = '1' and t.prizesended = '1' ");
		StringBuffer ticketSql = new StringBuffer();
		ticketSql.append("select sum(g.schemecost) from lotu_ticketgz g where 1=1 ");
		if (StringUtils.isNotBlank(startDate)) {
			sql.append("and t.prizesendtime >= to_date('" + startDate + "','yyyy-mm-dd hh24:mi:ss') ");
			ticketSql.append("and g.createtime >= to_date('" + startDate + "','yyyy-mm-dd hh24:mi:ss') ");
		}
		if (StringUtils.isNotBlank(endDate)) {
			sql.append("and t.prizesendtime <= to_date('" + endDate + "','yyyy-mm-dd hh24:mi:ss') ");
			ticketSql.append("and g.createtime <= to_date('" + endDate + "','yyyy-mm-dd hh24:mi:ss') ");
		}
		sql.append(" and t.won = 1 ");
		ticketSql.append("and g.ticketfinsh = 0 and g.lotterytype=" + this.getLottery().ordinal());
		sql.append("union " + ticketSql.toString());
		System.out.println("=============" + sql.toString());
		return issueDataDao.getSession().createSQLQuery(sql.toString()).list();
	}
}
